<?php
/**
 * @package DomDateTime
 * @author Oliver Leisalu
 */
class DomDateTimePeriod {
	
	/**
	 * Unix timestamp
	 *
	 * @var DomDateTime
	 */
	private $start;
	/**
	 * Unix timestamp
	 *
	 * @var DomDateTime
	 */
	private $end;
	
	/**
	 * @param Mixed $start Unix timestamp or DomDateTime object or anything that strtotime accepts.
	 * @param Mixed $end Unix timestamp or DomDateTime object or anything that strtotime accepts.
	 */
	public static function c( $start, $end ) {
		return new DomDateTimePeriod( $start, $end );
	}
	
	/**
	 * @param Mixed $start Unix timestamp or DomDateTime object or anything that strtotime accepts.
	 * @param Mixed $end Unix timestamp or DomDateTime object or anything that strtotime accepts.
	 */
	public function __construct( $start, $end ) {
		$this->start = new DomDateTime( (string)$start );
		$this->end = new DomDateTime( (string)$end );
	}
	
	/**
	 * Return number of whole workdays between dates. (times are not taken into account.)
	 * For example: from 2009-04-15 23:59:59 to 2009-04-22 00:00:00 there are 5 workdays. As first date only lasts one second, it still counts as a workday.
	 * 
	 * @return Int
	 */
	public function getNrOfWorkDays() {
		$workDays = 0;
		for( $i=$this->start->getStamp(), $end=$this->end->getStamp(); $i<$end; $i+=86400) {
			if( date('N', $i ) <= 5 )
				$workDays++;		
		}
		return $workDays;	
	}
	
	/**
	 * Return number of whole days between dates
	 *
	 * @return Int
	 */
	public function getNrOfDays() {
		return (int)floor((abs($this->end->getStamp() - $this->start->getStamp()))/86400);		
	}
		
	/**
	 * Return number of months between dates
	 * 
	 * If $includeEndDate is true then given a start date of 10/30/00 and an end date of 11/30/00, one month is returned; however, an end date of 11/29/00 returns a zero.
	 * 
	 * @param Bool $includeEndDate 
	 * @return Int
	 */
	public function getNrOfMonths( $includeEndDate = true ) {
		if( $includeEndDate === true )
			$round = $this->end->date('d') >= $this->start->date('d') ? 0 : -1;
		else
			$round = 0;		

		return $round + $this->getNrOfyears()*12 - $this->start->date('n') + $this->end->date('n');
	}
	
	/**
	 * Return number of years between dates
	 * @return Int
	 */
	public function getNrOfyears() {
		
		$startMonth = $this->start->date('m');
		$endMonth = $this->end->date('m');
		$startDay = $this->start->date('d');
		$endDay = $this->end->date('d');
		
		if( $this->start->getStamp() > $this->end->getStamp() ) {
			$a = $startMonth;
			$startMonth = $endMonth;
			$endMonth = $a;
			
			$a = $startDay;
			$startDay = $endDay;
			$endDay = $a;
			
			unset($a);	
		}
		
		if( $endMonth < $startMonth or ($endMonth == $startMonth && $endDay < $startDay) )
           $min = -1;
  
		return abs($this->start->date('Y') - $this->end->date('Y')) + $min;
	}
	/**
	 * Returns number of whole hours between dates
	 * @return Int
	 */
	public function getNrOfHours() {
		return (int)floor((abs($this->end->getStamp() - $this->start->getStamp()))/3600);
	}
	/**
	 * Returns number of whole minutes between dates. 
	 * @return Int
	 */
	public function getNrOfMinutes() {
		return (int)floor((abs($this->end->getStamp() - $this->start->getStamp()))/60);
	}
	/**
	 * Returns number of seconds between dates
	 * @return Int
	 */
	public function getNrOfSeconds() {
		return (int)(abs($this->end->getStamp() - $this->start->getStamp()));
	}
	
	/**
	 * Returns difference between times
	 * @return Array
	 * years
	 * months
	 * days
	 * hours
	 * minutes
	 * seconds
	 */
	public function getDif() {
		
		$start = $this->start->getStamp();
		$end = $this->end->getStamp();
		
		$result = array(
			'years' => abs(date('Y', $end) - date('Y',$start)),
			'months' => abs(date('m', $end) - date('m',$start)),
			'days' => abs(date('d', $end) - date('d',$start)),
			'hours' => abs(date('H', $end) - date('H',$start)),
			'minutes' => abs(date('i', $end) - date('i',$start)),
			'seconds' => abs(date('s', $end) - date('s',$start)),
		);
		
		return $result;
	}
	/**
	 * Returns difference between dates as a string. (Returns rounded result. Nr of days may wary.)
	 * @param Array $partsToInclude
	 * @param Int $maxNrOfElements How many date params to include. For example, if item is more than one year old, seconds might not matter anymore.
	 * @return String
	 */
	public function getDifString( array $partsToInclude = array('years','months','days','hours','minutes','seconds'), $maxNrOfElements = NULL ) {
		
		$start = $this->start->getStamp();
		$end = $this->end->getStamp();
		$seconds = abs($start-$end);
		
		$secondsInAYear = 365 * 24 * 3600;
		$secondsInAMonth = 30 * 24 * 3600;
		$secondsInADay = 24 * 3600;
		$secondsInAHour = 3600;
		$secondsInAMinute = 60;
		
		$dif = array();
		$dif['years'] = floor($seconds / $secondsInAYear);
		$seconds -= $dif['years'] * $secondsInAYear;
		$dif['months'] = floor($seconds / $secondsInAMonth);
		$seconds -= $dif['months'] * $secondsInAMonth;
		$dif['days'] = floor($seconds / $secondsInADay);
		$seconds -= $dif['days'] * $secondsInADay;
		$dif['hours'] = floor($seconds / $secondsInAHour);
		$seconds -= $dif['hours'] * $secondsInAHour;
		$dif['minutes'] = floor($seconds / $secondsInAMinute);
		$seconds -= $dif['minutes'] * $secondsInAMinute;
		$dif['seconds'] = $seconds;
		
		$str = '';
		$partsToInclude = arrayFlipTrue($partsToInclude);
		if( $maxNrOfElements === NULL ) {
			$maxNrOfElements = 100;
		}
		
		if( $dif['years'] > 0 && $partsToInclude['years']) {
			if( $maxNrOfElements > 0 ) {
				$str .= $dif['years'] . ' ' . ($dif['years'] == 1 ? ' year' : 'years').' ';
				$maxNrOfElements--;
			}
		} else {
			$dif['months'] += $dif['years'] * 12;
		}
		
		if( $dif['months'] > 0 && $partsToInclude['months'] ) {
			if( $maxNrOfElements > 0 ) {
				$str .= $dif['months'] . ' ' . ($dif['months'] == 1 ? ' month' : 'months').' ';
				$maxNrOfElements--;
			}
		} else {
			$dif['days'] += $dif['months'] * 30;
		}
		
		if( $dif['days'] > 0 && $partsToInclude['days'] ) {
			if( $maxNrOfElements > 0 ) {
				$str .= $dif['days'] . ' ' . ($dif['days'] == 1 ? ' day' : 'days').' ';
				$maxNrOfElements--;
			}
		} else {
			$dif['hours'] += $dif['days'] * 24;
		}
		
		if( $dif['hours'] > 0 && $partsToInclude['hours'] ) {
			if( $maxNrOfElements > 0 ) {
				$str .= $dif['hours'] . ' ' . ($dif['hours'] == 1 ? ' hour' : 'hours').' ';
				$maxNrOfElements--;
			}
		} else {
			$dif['minutes'] += $dif['hours'] * 60;
		}
		
		if( $dif['minutes'] > 0 && $partsToInclude['minutes'] ) {
			if( $maxNrOfElements > 0 ) {
				$str .= $dif['minutes'] . ' ' . ($dif['minutes'] == 1 ? ' minute' : 'minutes').' ';
				$maxNrOfElements--;
			}
		} else {
			$dif['seconds'] += $dif['minutes'] * 60;
		}
		
		if( $dif['seconds'] > 0 && $partsToInclude['seconds'] ) {
			if( $maxNrOfElements > 0 ) {
				$str .= $dif['seconds'] . ' ' . ($dif['seconds'] == 1 ? ' second' : 'seconds').' ';
				$maxNrOfElements--;
			}
		}
		
		return substr($str,0,-1);
	}
	
	/**
	 * @return DomDateTime
	 */
	public function getStart() {
		return $this->start;
	}
	
	/**
	 * @return DomDateTime
	 */
	public function getEnd() {
		return $this->end;
	}
	
	/**
	 * Returns months in period.
	 * 
	 * @return DomDateTimePeriod[]
	 */
	public function getMonths() {
		$months = array();
		$stamp = clone $this->start;
		for( $m=0, $nrOfMonths = $this->getNrOfMonths( false ); $m<$nrOfMonths; $m++ ) {
			//$stamp = new DomDateTime( mktime( 0, 0, 1, date('n', $this->start )+$m, 1, date('Y', $this->start ) ) );
			$months[] = new DomDateTimePeriod( $stamp->getMonthFirstDay(), $stamp->getMonthLastDay() );
			$stamp->add('+1 month');
		}
		return $months;
	}
	
	/**
	 * Returns days in period. day starts 00:00:00 and ends 23:59:59
	 * 
	 * @return DomDateTimePeriod[]
	 */
	public function getDays() {
		
		$days = array();
		$start = $this->start->getStamp();
		$end = $this->end->getStamp()+86400;
		
		for( $d=$start; $d<$end; $d+=86400) {
			$days[] = new DomDateTimePeriod( $d, $d + 86400 -1  );
		} 
		
		return $days;
	}
	
	
}

?>